package com.camel.docker.task;

import com.camel.docker.config.ApplicationConfig;
import com.camel.docker.config.WebSocketServer;
import com.camel.docker.entity.Result;
import com.camel.docker.entity.search.ContainerAddForm;
import com.camel.docker.enums.ResultEnum;
import com.camel.docker.service.DockerClientService;
import com.camel.docker.utils.*;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.BuildImageCmd;
import com.github.dockerjava.api.command.CreateContainerResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ResourceUtils;

import javax.print.Doc;
import java.io.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
public class CreatContainerFromGitTask extends Thread {
    public DockerClientService dockerClientService;
    private BufferedReader br = null;
    private PrintWriter pw = null;
    private WebSocketServer webSocketServer = null;
    private ContainerAddForm containerAddForm = null;
    private Writer writer = null;
    public static final String DOCKERFILE_NAME = "Dockerfile";


    //声明构造函数,接收客户端请求socket

    public CreatContainerFromGitTask(WebSocketServer s, ContainerAddForm form, DockerClientService dockerClientService) {
        this.webSocketServer = s;
        this.containerAddForm = form;
        this.dockerClientService = dockerClientService;
    }

    public void loadDockerFile(String filePath, ContainerAddForm addForm, FileWriter writer) throws IOException {
        this.writer = writer;
        writer.write("[INFO] ------------------------------------------------------------------------\n");
        writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + "开始拼写DOCKERFILE以创建需要的镜像...\n");
        File target = new File(filePath + "/" + DOCKERFILE_NAME);
        Resource resource = new ClassPathResource(DOCKERFILE_NAME);
        File src = resource.getFile();
        String cont = FileUtils.read(src);
        writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + " DOCKERFILE--start--\n");
        //对得到的内容进行处理
        cont = cont.replaceAll("IMAGE_PATH", addForm.getImage());
        if (StringUtils.isNotBlank(addForm.getDirective())) {
            List<String> result = new ArrayList<>();
            List<String> tmpDireciveList = CollectionUtils.arrayToList(addForm.getDirective().split(" "));
            tmpDireciveList = tmpDireciveList.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
            tmpDireciveList.forEach(s -> {
                result.add("\"" + s + "\"");
            });
            cont = cont.replaceAll("CMD_ARRAY_STR", result.toString());
        } else {
            cont = cont.replaceAll("CMD CMD_ARRAY_STR\n", "");
        }
        //更新源文件
        FileUtils.write(cont, target);
        writer.write(cont + "\n");
        writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + " DOCKERFILE--end----\n");
    }

    // 构建镜像
    public void buildImage(String dockerFilePath) throws IOException {
        this.writer.write("[INFO] ------------------------------------------------------------------------\n");
        this.writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + "开始根据DOCKERFILE构建需要的镜像...\n");
        DockerClient client = dockerClientService.connect();
        BuildImageCmd buildImageCmd = client.buildImageCmd(new File(dockerFilePath + "/" + DOCKERFILE_NAME));
        buildImageCmd.withBaseDirectory(new File(PropertyUtil.getProperty("local_repo_path")));
        buildImageCmd.withTag(containerAddForm.getName() + ":" + "v1");
        buildImageCmd.exec(new MyBuildImageCallback(containerAddForm, writer));
        this.writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + "DOCKER镜像构建完成...\n");
    }

    // 运行镜像和配置，生成容器
    public CreateContainerResponse runImage2Container(ContainerAddForm addForm) {
        DockerClient dockerClient = dockerClientService.connect();
        addForm.setImage(addForm.getName() + ":v1");
        CreateContainerResponse response = null;
        try {
            DockerUtils dockerUtils = DockerUtils.getInstance();
            response = dockerUtils.createContainer(addForm, dockerClient);

        } catch (Exception e) {
            ApplicationConfig.containerCreatedVariable.set(true);
            try {
                this.writer.close();
            } catch (IOException ex) {
                System.err.println(ex.getMessage());
            }
            Result result = DockerClientExceptionUtils.trans(e.getMessage(), addForm);
        }
        return response;
    }

    public void startContainer(CreateContainerResponse response) {
        DockerClient dockerClient = dockerClientService.connect();
        if (!org.springframework.util.StringUtils.isEmpty(response.getId())) {
            dockerClient.startContainerCmd(response.getId()).exec();
        }
    }

    @Override
    public void run() {
        // 创建临时文件，保存日志信息
        String projectPath = PropertyUtil.getProperty("local_repo_path");
        String localCodePath = PropertyUtil.getProperty("init_local_code_dir");
        File file = new File(localCodePath + "/logs/" + containerAddForm.getName() + ".log");
        FileWriter writer = null;
        try {
            file.createNewFile();
            new LogFileTailerTask(webSocketServer, file.getAbsolutePath()).start();
            writer = new FileWriter(file);

            writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + "开始准备创建容器需要的文件...\n");
            writer.write("[INFO] ------------------------------------------------------------------------\n");
            writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + "正在使用GIT下载源码信息...\n");
            JGitUtil.cloneReposeitory(containerAddForm.getStartInfo().getAddr(), writer);
            writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + "源码下载完毕, 文件信息在 [" + projectPath + "] 目录下\n");
            writer.write("[INFO] ------------------------------------------------------------------------\n");
            writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + "开始使用MAVEN打包源码...\n");
            MavenUtils.packageProject(webSocketServer, writer);
            writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + "打包成功\n");
            writer.write("[INFO] ------------------------------------------------------------------------\n");
            DockerClient client = dockerClientService.connect();
            loadDockerFile(projectPath, containerAddForm, writer);
            buildImage(projectPath);
            writer.write("[INFO] --------------------------END----------------------------------------------\n");
        } catch (GitAPIException ex) {
            ex.printStackTrace();
            try {
                writer.write("[ERROR] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + GitClientExceptionUtils.trans(ex.getMessage()).getMsg() + "\n");
            } catch (IOException e) {
                e.printStackTrace();
            }
            log.error("下载代码信息失败");
            ApplicationConfig.containerCreatedVariable.set(true);
        } catch (FileNotFoundException ex) {
            log.error("未找到文件夹，请确认");
            ApplicationConfig.containerCreatedVariable.set(true);
        } catch (IOException ex) {
            log.error("保存日志文件信息出错，或DOCKER模板不存在");
            ApplicationConfig.containerCreatedVariable.set(true);
        } finally {
            try {
                if (ApplicationConfig.containerCreatedVariable.get()) {
                    writer.close();
                }
            } catch (IOException e) {
                log.error("关闭日志失败");
            }
        }
    }

    @Override
    public void destroy() {
        System.out.println("===============================================================");
        try {
            this.writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Data
    public class MyBuildImageCallback implements ResultCallback {
        private ContainerAddForm addForm;
        private Writer writer;

        public MyBuildImageCallback(ContainerAddForm addForm, Writer writer) {
            this.addForm = addForm;
            this.writer = writer;
        }

        @Override
        public void onStart(Closeable closeable) {

        }

        @Override
        public void onNext(Object object) {

        }

        @Override
        public void onError(Throwable throwable) {

        }

        @Override
        public void onComplete() {
            try {
                writer.write("[INFO] ------------------------------------------------------------------------\n");
                writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + "创建容器开始...\n");
                CreateContainerResponse response = runImage2Container(addForm);
                writer.write("[INFO] ------------------------------------------------------------------------\n");
                writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + "创建容器完成...\n");
                writer.write("[INFO] ------------------------------------------------------------------------\n");
                writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + "正在启动容器...\n");
                startContainer(response);
                writer.write("[INFO] " + DateTimeUtils.getLogCurrentTime(this.getClass()) + "容器启动完成\n");
                writer.write("[INFO] ------------------------------------------------------------------------\n");
                ApplicationConfig.containerCreatedVariable.set(true);
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void close() throws IOException {

        }
    }
}
